// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2025 Andrea Cervesato <andrea.cervesato@suse.com>
 */

/*\
 * Test for CVE-2025-21756 fixed in kernel v6.14:
 * fcdd2242c023 vsock: Keep the binding until socket destruction
 *
 * Reproducer based on:
 * https://lore.kernel.org/all/20250128-vsock-transport-vs-autobind-v3-5-1cf57065b770@rbox.co/
 *
 * Beware, this test will crash the system.
 */

#include "tst_test.h"
#include "lapi/vm_sockets.h"

#define MAX_PORT_RETRIES	24
#define VMADDR_CID_NONEXISTING	42

static int vsock_bind(unsigned int cid, unsigned int port, int type)
{
	int sock;

	struct sockaddr_vm sa = {
		.svm_family = AF_VSOCK,
		.svm_cid = cid,
		.svm_port = port,
	};

	sock = SAFE_SOCKET(AF_VSOCK, type, 0);

	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
		if (errno == EINVAL)
			tst_brk(TCONF, "VM socket is not supported");

		tst_brk(TBROK | TERRNO, "bind() error");
	}

	return sock;
}

static void run(void)
{
	int sockets[MAX_PORT_RETRIES];
	struct sockaddr_vm addr;
	int socket, sock_count;
	socklen_t alen;

	socket = vsock_bind(VMADDR_CID_LOCAL, VMADDR_PORT_ANY, SOCK_SEQPACKET);

	alen = sizeof(addr);
	SAFE_GETSOCKNAME(socket, (struct sockaddr *)&addr, &alen);

	for (sock_count = 0; sock_count < MAX_PORT_RETRIES; ++sock_count) {
		sockets[sock_count] = vsock_bind(VMADDR_CID_ANY,
				   ++addr.svm_port, SOCK_SEQPACKET);
	}

	SAFE_CLOSE(socket);

	socket = SAFE_SOCKET(AF_VSOCK, SOCK_SEQPACKET, 0);
	if (!connect(socket, (struct sockaddr *)&addr, alen))
		tst_brk(TBROK, "Unexpected connect() #1 success");

	addr.svm_cid = VMADDR_CID_NONEXISTING;
	if (!connect(socket, (struct sockaddr *)&addr, alen))
		tst_brk(TBROK, "Unexpected connect() #2 success");

	addr.svm_cid = VMADDR_CID_LOCAL;
	addr.svm_port = VMADDR_PORT_ANY;

	/* Vulnerable system may crash now. */
	SAFE_BIND(socket, (struct sockaddr *)&addr, alen);
	SAFE_CLOSE(socket);

	if (tst_taint_check())
		tst_res(TFAIL, "Kernel is vulnerable");
	else
		tst_res(TPASS, "Kernel survived after socket unbinding");

	while (sock_count--)
		SAFE_CLOSE(sockets[sock_count]);
}

static struct tst_test test = {
	.test_all = run,
	.taint_check = TST_TAINT_W | TST_TAINT_D,
	.tags = (const struct tst_tag[]) {
		{"linux-git", "fcdd2242c023"},
		{"CVE", "2025-21756"},
		{}
	},
};
